<!doctype html><html lang=cn-zh><head><meta charset=UTF-8><meta name=viewport content="width=device-width,initial-scale=1"><meta http-equiv=X-UA-Compatible content="IE=edge"><style type=text/css>body{font-family:monospace}</style><title>[Python]Uvicorn初体验</title>
<meta name=description content="A blog maintained by Vimiix."><link rel=stylesheet href=/css/style.css><script>var _hmt=_hmt||[];(function(){var e,t=document.createElement("script");t.src="https://hm.baidu.com/hm.js?7c24231917964240bae97e813810620c",e=document.getElementsByTagName("script")[0],e.parentNode.insertBefore(t,e)})()</script></head><body><header>====================<br>== Hi, I'm Vimiix ==<br>====================<div style=float:right;color:gray;font-size:x-large>Get hands dirty.</div><br><p><nav><a href=https://www.vimiix.com/><b>首页</b></a>.
<a href=/posts/><b>文章列表</b></a>.
<a href=/projects/><b>开源项目</b></a>.
<a href=/tags/><b>标签</b></a>.
<a href=/friends/><b>友链</b></a>.
<a href=/about/><b>关于我</b></a>.
<a href=/index.xml><b>RSS</b></a>.</nav></p></header><main><article><h1>[Python]Uvicorn初体验</h1><b><time>2018.02.26 12:20</time></b>
<a href=/tags/uvicorn>uvicorn</a>
<a href=/tags/web>web</a>
<a href=/tags/python>Python</a>
<a href=/tags/asyncio>asyncio</a><div><h2 id=uvicorn-简介>uvicorn 简介</h2><p><code>uvicorn</code>是一个基于<code>asyncio</code>开发的一个轻量级高效的 web 服务器框架。</p><blockquote><p>官网：<a href=http://www.uvicorn.org>http://www.uvicorn.org</a></p></blockquote><p><code>uvicorn</code> 设计的初衷是想要实现两个目标：</p><ul><li>使用<a href=https://github.com/MagicStack/uvloop><code>uvloop</code></a>和<a href=https://github.com/MagicStack/httptools><code>httptools</code></a>实现一个极速的<code>asyncio</code>服务器。</li><li>实现一个基于<a href=http://channels.readthedocs.io/en/stable/asgi.html><code>ASGI(异步服务器网关接口)</code></a>的最小的应用程序接口。</li></ul><p>它目前支持<code>http</code>，<code>websockets</code>，<code>Pub/Sub</code> 广播，并且可以扩展到其他协议和消息类型。</p><h2 id=安装使用>安装使用</h2><p><code>uvicorn</code> 仅支持 python 3.5.3 以上版本，我们可以通过 pip3 来快速的安装。</p><h6 id=tip建议和我一样直接使用pip3来安装就不用关心系统默认版本了>Tip：建议和我一样，直接使用<code>pip3</code>来安装，就不用关心系统默认版本了。</h6><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-python data-lang=python><span style=display:flex><span><span style=color:#960050;background-color:#1e0010>➜</span> pip3 install uvicorn
</span></span><span style=display:flex><span>	<span style=color:#f92672>...</span>
</span></span><span style=display:flex><span>Successfully installed gunicorn<span style=color:#f92672>-</span><span style=color:#ae81ff>19.7.1</span> httptools<span style=color:#f92672>-</span><span style=color:#ae81ff>0.0.10</span>
</span></span><span style=display:flex><span>uvicorn<span style=color:#f92672>-</span><span style=color:#ae81ff>0.0.15</span> uvloop<span style=color:#f92672>-</span><span style=color:#ae81ff>0.9.1</span> websockets<span style=color:#f92672>-</span><span style=color:#ae81ff>4.0.1</span>
</span></span></code></pre></div><p>安装成功以后。就可以来编写我们的服务器应用代码了。</p><p>先创建一个应用文件<code>app.py</code>（名字可以自取）</p><p>在这个文件中，来编写一个简单的服务器应用。</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-python data-lang=python><span style=display:flex><span><span style=color:#ae81ff>1</span> <span style=color:#75715e># coding:utf-8</span>
</span></span><span style=display:flex><span><span style=color:#ae81ff>2</span>
</span></span><span style=display:flex><span><span style=color:#ae81ff>3</span> <span style=color:#66d9ef>async</span> <span style=color:#66d9ef>def</span> <span style=color:#a6e22e>hello_world</span>(message, channels):
</span></span><span style=display:flex><span><span style=color:#ae81ff>4</span>     content <span style=color:#f92672>=</span> <span style=color:#e6db74>b</span><span style=color:#e6db74>&#39;&lt;h1&gt;Hello World&lt;/h1&gt;&#39;</span>
</span></span><span style=display:flex><span><span style=color:#ae81ff>5</span>     resp <span style=color:#f92672>=</span> {
</span></span><span style=display:flex><span><span style=color:#ae81ff>6</span>         <span style=color:#e6db74>&#39;status&#39;</span>: <span style=color:#ae81ff>200</span>,
</span></span><span style=display:flex><span><span style=color:#ae81ff>7</span>         <span style=color:#e6db74>&#39;headers&#39;</span>: [[<span style=color:#e6db74>b</span><span style=color:#e6db74>&#39;content-type&#39;</span>, <span style=color:#e6db74>b</span><span style=color:#e6db74>&#39;text/html&#39;</span>],],
</span></span><span style=display:flex><span><span style=color:#ae81ff>8</span>         <span style=color:#e6db74>&#39;content&#39;</span>: content,
</span></span><span style=display:flex><span><span style=color:#ae81ff>9</span>     }
</span></span><span style=display:flex><span><span style=color:#ae81ff>10</span>     <span style=color:#66d9ef>await</span> channels[<span style=color:#e6db74>&#39;reply&#39;</span>]<span style=color:#f92672>.</span>send(resp)
</span></span><span style=display:flex><span><span style=color:#ae81ff>11</span>
</span></span></code></pre></div><p>写好以后，先来尝试运行一下，跑通了再看代码中具体内容的含义。</p><p>运行方式是： <code>uvicorn 文件名:callable对象名</code></p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-python data-lang=python><span style=display:flex><span><span style=color:#960050;background-color:#1e0010>➜</span> uvicorn app:hello_world
</span></span></code></pre></div><p>提示下面的内容就表示服务器启动成功了</p><p>（信息中包括了访问地址和端口号，以及 worker 运行的线程 id）</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-python data-lang=python><span style=display:flex><span>[<span style=color:#ae81ff>2018</span><span style=color:#f92672>-</span><span style=color:#ae81ff>02</span><span style=color:#f92672>-</span><span style=color:#ae81ff>26</span> <span style=color:#ae81ff>00</span>:<span style=color:#ae81ff>48</span>:<span style=color:#ae81ff>52</span> <span style=color:#f92672>+</span><span style=color:#ae81ff>0800</span>] [<span style=color:#ae81ff>55984</span>] [INFO] Starting gunicorn <span style=color:#ae81ff>19.7.1</span>
</span></span><span style=display:flex><span>[<span style=color:#ae81ff>2018</span><span style=color:#f92672>-</span><span style=color:#ae81ff>02</span><span style=color:#f92672>-</span><span style=color:#ae81ff>26</span> <span style=color:#ae81ff>00</span>:<span style=color:#ae81ff>48</span>:<span style=color:#ae81ff>52</span> <span style=color:#f92672>+</span><span style=color:#ae81ff>0800</span>] [<span style=color:#ae81ff>55984</span>] [INFO] Listening at: http:<span style=color:#f92672>//</span><span style=color:#ae81ff>127.0.0.1</span>:<span style=color:#ae81ff>8000</span> (<span style=color:#ae81ff>55984</span>)
</span></span><span style=display:flex><span>[<span style=color:#ae81ff>2018</span><span style=color:#f92672>-</span><span style=color:#ae81ff>02</span><span style=color:#f92672>-</span><span style=color:#ae81ff>26</span> <span style=color:#ae81ff>00</span>:<span style=color:#ae81ff>48</span>:<span style=color:#ae81ff>52</span> <span style=color:#f92672>+</span><span style=color:#ae81ff>0800</span>] [<span style=color:#ae81ff>55984</span>] [INFO] Using worker: uvicorn <span style=color:#ae81ff>0.0.15</span>
</span></span><span style=display:flex><span>[<span style=color:#ae81ff>2018</span><span style=color:#f92672>-</span><span style=color:#ae81ff>02</span><span style=color:#f92672>-</span><span style=color:#ae81ff>26</span> <span style=color:#ae81ff>00</span>:<span style=color:#ae81ff>48</span>:<span style=color:#ae81ff>52</span> <span style=color:#f92672>+</span><span style=color:#ae81ff>0800</span>] [<span style=color:#ae81ff>55987</span>] [INFO] Booting worker <span style=color:#66d9ef>with</span> pid: <span style=color:#ae81ff>55987</span>
</span></span></code></pre></div><p>这时候我们在浏览器中访问<code>http://127.0.0.1:8000</code>,会看到网页上显示出 <code>h1</code> 号字体的<code>Hello World</code>，也就是我们代码中定义的 <code>content</code> 的字符串内容。</p><p>OK，服务器跑起来了，接下来，我们来看一下代码是如何将内容返回给浏览器的。</p><h2 id=接口分析>接口分析</h2><p>在代码中我们定义了一个协程函数，ASGI 的协议要求应用应该对外暴露一个可接受 <code>message</code> 和 <code>channels</code> 这两个参数的协程可调用对象(callable)：</p><ul><li><code>message</code>：一个 ASGI 消息（但有区别，见下文）</li><li><code>channels</code>：一个字典（ <code>&lt;unicode string> : &lt;channel interface></code> ）</li></ul><p><code>&lt;channel interface></code> 是具有以下属性的对象：</p><ul><li>.send(message) 一个协程，用于发送返回的消息。可选</li><li>.receive() 一个协程，用于接收进来的消息。可选</li><li>.name 一个<code>unicode</code>字符串，channel 的唯一标识。可选</li></ul><p>uvicorn 中的<code>message</code>区别于 ASGI 中的消息：</p><ul><li>消息还包括一个<code>channel</code>关键字，区别消息类型，例如:<code>'channel':'http.request'</code></li><li>消息不包括例如<code>reply_channel</code>或<code>body_channel</code>这样的<code>channel</code>名称，，而是<code>channels</code>字典可以查看允许的<code>channel</code>类型。</li></ul><h4 id=举例>举例：</h4><p>传进来的一个 HTTP 请求可能会是类似下面这样的<code>message</code>和 <code>channels</code>.</p><p><code>message</code></p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-python data-lang=python><span style=display:flex><span>{
</span></span><span style=display:flex><span>    <span style=color:#e6db74>&#39;channel&#39;</span>: <span style=color:#e6db74>&#39;http.request&#39;</span>,
</span></span><span style=display:flex><span>    <span style=color:#e6db74>&#39;scheme&#39;</span>: <span style=color:#e6db74>&#39;http&#39;</span>,
</span></span><span style=display:flex><span>    <span style=color:#e6db74>&#39;root_path&#39;</span>: <span style=color:#e6db74>&#39;&#39;</span>,
</span></span><span style=display:flex><span>    <span style=color:#e6db74>&#39;server&#39;</span>: (<span style=color:#e6db74>&#39;127.0.0.1&#39;</span>, <span style=color:#ae81ff>8000</span>),
</span></span><span style=display:flex><span>    <span style=color:#e6db74>&#39;http_version&#39;</span>: <span style=color:#e6db74>&#39;1.1&#39;</span>,
</span></span><span style=display:flex><span>    <span style=color:#e6db74>&#39;method&#39;</span>: <span style=color:#e6db74>&#39;GET&#39;</span>,
</span></span><span style=display:flex><span>    <span style=color:#e6db74>&#39;path&#39;</span>: <span style=color:#e6db74>&#39;/&#39;</span>,
</span></span><span style=display:flex><span>    <span style=color:#e6db74>&#39;headers&#39;</span>: [
</span></span><span style=display:flex><span>        [<span style=color:#e6db74>b</span><span style=color:#e6db74>&#39;host&#39;</span>, <span style=color:#e6db74>b</span><span style=color:#e6db74>&#39;127.0.0.1:8000&#39;</span>],
</span></span><span style=display:flex><span>        [<span style=color:#e6db74>b</span><span style=color:#e6db74>&#39;user-agent&#39;</span>, <span style=color:#e6db74>b</span><span style=color:#e6db74>&#39;curl/7.51.0&#39;</span>],
</span></span><span style=display:flex><span>        [<span style=color:#e6db74>b</span><span style=color:#e6db74>&#39;accept&#39;</span>, <span style=color:#e6db74>b</span><span style=color:#e6db74>&#39;*/*&#39;</span>]
</span></span><span style=display:flex><span>    ]
</span></span><span style=display:flex><span>}
</span></span></code></pre></div><p><code>channels</code></p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-python data-lang=python><span style=display:flex><span>{
</span></span><span style=display:flex><span>    <span style=color:#e6db74>&#39;reply&#39;</span>: <span style=color:#f92672>&lt;</span>ReplyChannel<span style=color:#f92672>&gt;</span>
</span></span><span style=display:flex><span>}
</span></span></code></pre></div><p>为了做出响应，应用程序需要向<code>reply</code>的<code>channel</code>发送（<code>.send()</code>）一个 http 响应，例如：</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-python data-lang=python><span style=display:flex><span><span style=color:#66d9ef>await</span> channels[<span style=color:#e6db74>&#39;reply&#39;</span>]<span style=color:#f92672>.</span>send({
</span></span><span style=display:flex><span>    <span style=color:#e6db74>&#39;status&#39;</span>: <span style=color:#ae81ff>200</span>,
</span></span><span style=display:flex><span>    <span style=color:#e6db74>&#39;headers&#39;</span>: [
</span></span><span style=display:flex><span>        [<span style=color:#e6db74>b</span><span style=color:#e6db74>&#39;content-type&#39;</span>, <span style=color:#e6db74>b</span><span style=color:#e6db74>&#39;text/plain&#39;</span>],
</span></span><span style=display:flex><span>    ],
</span></span><span style=display:flex><span>    <span style=color:#e6db74>&#39;content&#39;</span>: <span style=color:#e6db74>b</span><span style=color:#e6db74>&#39;Hello, world&#39;</span>
</span></span><span style=display:flex><span>})
</span></span></code></pre></div></div></article></main><aside><div><div><h3>LATEST POSTS</h3></div><div><ul><li><a href=/posts/2025-10-16-kubernetes-apiserver-authorization-mechanism/>Kubernetes APIServer 鉴权机制</a></li><li><a href=/posts/2025-09-30-kubernetes-apiserver-authentication-mechanism/>Kubernetes APIServer 认证机制</a></li><li><a href=/posts/2024-12-16-deploy-kubernetes-by-kubeadm/>使用 kubeadm 搭建 kubernetes 集群</a></li><li><a href=/posts/2024-09-20-how-to-code-review/>如何做code review</a></li><li><a href=/posts/2024-08-12-weakref-in-python/>Python中的弱引用</a></li></ul></div></div></aside><footer><p>Social Links:
<a href=https://github.com/vimiix><b>Github</b></a>.
<a href=https://www.douban.com/people/vimiix/><b>Douban</b></a>.
<a href=mailto:i@vimiix.com><b>Email</b></a>.<br><hr>&copy; 2017-2025
Vimiix Yao; All rights reserved.
<a href=https://beian.miit.gov.cn/>京ICP备19015214号-1</a></p><script src=https://l2dwidget.js.org/lib/L2Dwidget.min.js></script><script>L2Dwidget.init({model:{jsonPath:"https://unpkg.com/live2d-widget-model-tororo@1.0.5/assets/tororo.model.json"}})</script></footer></body></html>